package org.NooLab.docserver.applet;



import processing.core.PApplet;


import org.NooLab.docserver.FileChangeIntf;
import org.NooLab.docserver.app.DocoProcessControlIntf;
import org.NooLab.docserver.app.up.DocoServStartup;
import org.NooLab.docserver.app.up.IniProperties;

import org.NooLab.docserver.main.DocumentObserverApplicationEventIntf;
import org.NooLab.docserver.main.DocumentObserverFactory;
import org.NooLab.docserver.properties.DocoControlProperties;
import org.NooLab.docserver.properties.DocoPropertiesHandler;
import org.NooLab.docserver.properties.DocumentObserverProperties;
import org.NooLab.docserver.properties.PersistenceSettings;
import org.NooLab.docserver.storage.DocoDataBase;
import org.NooLab.structures.InstanceProcessControlIntf;
import org.NooLab.utilities.logging.LogControl;
import org.NooLab.utilities.logging.PrintLog;




public class DocumentObserverApplet extends PApplet{

	private static final long serialVersionUID = -1206243743183135630L;

	DocumentObserver_Instance docoInstance ;
	String sourceForProperties = "" ;
	PApplet applet;

	public DocumentObserverApplet(){
		super();
	}
	
	public PApplet getInstance(){
		applet = this;
		
		//init();
		
		// applet.setVisible(true) ;
		PApplet.main( new String[] { "org.NooLab.docserver.applet.DocumentObserverApplet"});
		
		PrintLog.Delay(200) ;
		
		
		return this;
	}

	
	public void setup(){
		 
		applet = this;
		background( 208,188,188);
		 
		size(220,200);
		
		// applet.setLocation(50, 50) ;
		setLocation( 50, 50 );
		if (frame!=null){
		frame.setLocation( 50, 50 ); 
		frame.setTitle(" Document Observer") ;
		}
		
		try {
			DocoServStartup.setApplicationID("DocServer",this.getClass()); 
		
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return;
		}
		  
		showKeyCommands();
		
		
		draw();
		
		// this.frame.setTitle("DocumentObserver") ;
		// start this in its own thread...
		
		// use LogControl to control output of log messages
		LogControl.globeScope = 2; // 1=local definitions  default = 2
		LogControl.Level = 1 ;     // the larger the more detailed logging... (could be really verbose!)
  
		

	}
	
	public void draw(){
		background( 218,228,238);
		if (frame!=null){
			frame.setLocation( 50, 50 ); 
		}
		this.noLoop();
	}

	private void showKeyCommands(){

		println();
		println("Welcome to DocumentObserver!\n");
		println("the following key commands are available for minimalistic user-based control...");
		println();
		println("   c  ->  start collecting file names into a persistent todo list ");
		println("   s  ->  start observation ");
		println();
		println("   a  ->  add folder to list of observations ");
		println("   r  ->  exclude folder from list of observations ");
		println("   e  ->  edit configuration file"); // should use an xml editor
		println();
		println("   D  ->  clear documents table  ");
		println("   d  ->  start database server  ");
		println();
		println("   n  ->  create a new (sub-)project in the current project space, extending the last corpus used  ");
		println("   o  ->  open existing project for activating previous settings ");
		println();
		println("   p  ->  select a different base folder (=project space) for all subsequent projects (very first step!) ");
		println("   P  ->  create a new project space  ");
		println();
		println("   x  ->  exit");
		println();
		println("------------------------------------------------------------------");
		println();

		_showCurrentInputSettings();
	}
	
	private void _showCurrentInputSettings(){
		String qs="";
		if (DocoServStartup.isDataStoreAvailable()==false){
			qs = "not " ;
		}
		println("current project : "+ DocoServStartup.getLastProjectName());
		
		{
		println("config source   : "+ DocoServStartup.getLastDataSet() ) ;
		}

	}
	
	public void keyPressed() {

		
		if (key=='a'){
			looping = false;
			this.noLoop();

			docoInstance.addRecord() ;
			delay(100);
			
			looping = true;
			this.loop();
		}
		
		if (key=='c'){
			// start collecting file names
		 
			looping = false;
			this.noLoop();
			
			String cfg = prepareEngines();
			
				
			if (cfg.length()>0){
				looping = true;
				this.loop();

				startEngines(1); // 0= service, 1=dedicated run
			}
		}
		
		if (key=='s'){
			// start observation
			key=0;
			 
			looping = false;

			startEngines(0) ; // 0= service, 1=dedicated run
			
			looping = true;
			this.loop() ;
		}

		if (key=='D'){
			// clear table "documents"
			// clearDataBaseTable("documents");
		}
		if (key=='d'){
			// clear table "documents"
			docoInstance.startDataBaseServer() ;
		}
		

		if (key=='o'){
			// open project
			openProject();
		}
		if (key=='n'){
			// new project, using current settings
			openProject();
		}
		
		if (key=='p'){ //select a different base folder for projects
			 
			looping = false;
			key=0;
			String selectedFolder;
			try {
				selectedFolder = DocoServStartup.selectProjectHome();

			} catch (Exception e) {
				System.err.println(e.getMessage());
				return;
			}
			System.err.println("Folder selected as base folder for projects : "+selectedFolder);
			
			looping = true;
			this.loop() ;
		}
		
		if (key=='P'){ // capital "P": // create new project space...
			
			looping = false;
			this.noLoop();
			key=0;
			try {
			
				String psLabel = DocoServStartup.getNewProjectSpaceLabel();
				
				if (DocumentObserverFactory.projectSpaceExists(psLabel)){
					return;
				}

				IniProperties.lastProjectName = psLabel ;
				DocumentObserverFactory.completeProjectSpace();
			 
				System.err.println("Folder selected as new project space : "+DocoServStartup.getProjectBasePath() + psLabel);
				
				// now we offer a file selection dialog, which will copy 
				DocumentObserverFactory.organizeRawProjectData();
					
				
				
			} catch (Exception e) {
				System.err.println(e.getMessage());
			}
			
			looping = true;
			this.loop() ;
			
		}

		if (key=='x'){
			IniProperties.saveIniProperties();
			if (docoInstance!=null){
				docoInstance.close();
				delay(500);
			}
			System.exit(0);
		}
	}
	
	
	private String prepareEngines(){
		
		boolean rB;
		String cfg="" ;
		
		
		if (IniProperties.lastProjectName.length()==0){
			try {
				// this is just a config file in the respective directory
				DocoServStartup.selectActiveProject(); // project is the directory
				rB=true;
			} catch (Exception e) {
				rB=false;
			}
		}else{
			rB=true;
		}
		String prj = DocoServStartup.getLastProjectName() ;
		
		// datasrc = DocoServStartup.introduceDataSet("") ;
		// "docoserv-config.xml"
				
		cfg = DocoServStartup.getLastDataSet();

		if ((cfg==null) || (cfg.length()==0)){
			
			String datasrc = DocoServStartup.introduceDataSet("docoserv-config.xml") ;
			cfg = DocoServStartup.getLastDataSet();
		}

		return cfg;
	}
	
	
	/**
	 * 
	 * @param somtype 0=default: simple, single run; 1=som optimizer
	 * @param lastProjectName2 
	 */
	private void startEngines( int worktype ){
		
		if (docoInstance!=null){
			return;
		}
		println();
		println("starting project <"+ IniProperties.lastProjectName+"> ...");
		
		if (worktype==0){
			worktype = DocumentObserverFactory._INSTANCE_TYPE_SERVICE ;
		}else{
			worktype = DocumentObserverFactory._INSTANCE_TYPE_EXPLICIT ;
		}
		
		docoInstance = new DocumentObserver_Instance( DocumentObserverFactory._GLUE_MODULE_ENV_NONE,
			 							 	 		 sourceForProperties ) ;
		if (worktype>=0){
			docoInstance.startInstance(worktype) ;
		}
	}
	
	private void openProject(){
		
		looping = false;
		this.noLoop(); // that's mandatory, otherwise, the dialog won't be drawn
		
		try {
		
			DocoServStartup.selectActiveProject();
	
		} catch (Exception e) {
			e.printStackTrace();
		}
		 
		looping = true;
		this.loop() ;
		// http://code.google.com/p/processing/source/checkout
	}

	private void clearDataBaseTable( String tablename){
		docoInstance.clearDataBaseTable( tablename) ;
	}
	
	
}


class DocumentObserver_Instance implements   Runnable,
							  				// for messages from the engine to the outer application layers like this module
											DocumentObserverApplicationEventIntf{


	DocumentObserverFactory    docoFactory;
	DocumentObserverProperties docoProperties;
	DocoPropertiesHandler docoPropertiesDefaults;
	
	String sourceForProperties = "";
	
	Thread docoThrd;
	private String lastProjectName;
	private int instanceType;
	private InstanceProcessControlIntf docoProcessControl;
	
	
	
	
	// ------------------------------------------------------------------------
	public DocumentObserver_Instance( int gluemode, String propertiesSource ){
		
		sourceForProperties = propertiesSource;
		
		docoThrd = new Thread(this, "DocoInstance");
	}


	public void close() {

		if (docoProcessControl!=null){
			docoProcessControl.close(0);
		}
		
	}


	public void startInstance( int worktype) {

		instanceType = worktype ;
		
		lastProjectName = IniProperties.lastProjectName ;
		docoThrd.start();
	
	}
	// ------------------------------------------------------------------------
	
	
	
	@Override
	public void run() {

		if (instanceType > DocumentObserverFactory._INSTANCE_TYPE_DEFAULT){
			
			prepareDocumentObserver();
			
			try {
			
				createStartTask();

			} catch (Exception e) {
				e.printStackTrace();
			}
		}else{
			System.err.println("Can not start the instance using this type code ("+instanceType+").");
		}
		
	}
	
	public void issueUserbreak() {
		//  
		if (docoProcessControl!=null){
			docoProcessControl.interrupt(0);
		}else{
			System.err.println("Nothing to stop, <somProcessControl> was not up and running...");
		}
	}

	
	public void clearDataBaseTable(String tablename) {
		
		prepareDocumentObserver();
		docoFactory.getDocoDataBase().startServer();
		docoFactory.getDocoDataBase().clearTable(tablename) ;
	}

	public void startDataBaseServer() {
		prepareDocumentObserver();
		docoFactory.getDocoDataBase().startServer();
	}


	private void prepareDocumentObserver(){

		/*
		 * the basic scheme is always: 
		 * 		1. create properties object (is also loading), 
		 * 		2. create factory, prepare core instance
		 * 		3. start core instance, which contains a task loop
		 *      4. create a task
		 *      5. core will accept task and dispatch to respective objects
		 *      
		 *      
		 * only in this way we will get an application, that could always be on, if needed,
		 * and acting as a service
		 * 
		 */
		sourceForProperties = "" ;
		docoProperties = DocumentObserverProperties.getInstance( sourceForProperties );
		
		// alternatively... load from /resources in jar ...
		 
		
		if ((sourceForProperties.length()==0) || ( docoProperties.isInitializationOK()==false )){
		
			docoPropertiesDefaults = new DocoPropertiesHandler(docoProperties);
			
			explicitlySettingProperties();
			 
		}

		docoFactory = DocumentObserverFactory.getInstance(docoProperties);   // creating the factory; 	
		
		
		docoFactory.saveStartupTrace( instanceType, 
									  docoPropertiesDefaults.getStartupTraceInfo() );

		// now we could save the settings for ensuring smooth restart capabilities
		
		docoProperties.save();
		


	}


	private void createStartTask( ) throws Exception{
	
		int processCount;
		docoProcessControl = docoFactory.getDocoProcessControl() ;
		processCount = docoFactory.startDocumentObserver( instanceType,this );
		
		if (processCount>0){
			// issue a message to the framework
			
		}
	}
	
	
	
	private void explicitlySettingProperties(){
		
		PersistenceSettings ps;


		
		try {
			// important to identify the startup trace... and persistence setting
			String instance = "doco";

			if (instanceType<=0){
				// instanceType = dc.
			}
			
			docoPropertiesDefaults.setInstance( instance ,0 ); 
			
			docoPropertiesDefaults.setConfigFile( IniProperties.dataSource );

			
			docoPropertiesDefaults.initializeDefaults();
			// loading xml, check texx for reading this stuff
			docoPropertiesDefaults.initializeDefaultsFromFile( IniProperties.dataSource ) ;
			

			
			// controlling the granularity/verbosity of console messages 
			if (LogControl.Level>2){
				docoProperties.setShowDocoProgress( DocumentObserverProperties._DOCO_DISPLAY_PROGRESS_STEPS ) ; // default: _SOMDISPLAY_PROGRESS_BASIC
			}
			docoProperties.setShowDocoProgress( DocumentObserverProperties._DOCO_DISPLAY_PROGRESS_STEPS );
			
			docoProperties.setMultiProcessingLevel(0); 
			
			// if we set this to true, we have to restart the observer from time to time, once each day or so
			docoProperties.setObserveOnlyUsedDirectories( true ) ;
			
			DocoControlProperties dc = docoProperties.getControlProperties();
			dc.readSettings(""); // if no file is given, the default name and location will be assumed

			
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("The system will exit.");
			System.exit(-7) ;
		}
		
		// set ready flag
		docoProperties.setInitializationOK(true) ;						
	}
	
	public void addRecord(){
		DocoDataBase docoDataBase = docoFactory.getDocoDataBase();
		//docoDataBase.addTestRecord();
	}


	@Override
	public void setChangeEventCount(int count) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void setNotifications(String[] changetypes) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void setLastRecordedChange(FileChangeIntf change) {

		String changeDescr , currFilename, prevFilename;
		
		changeDescr = change.getDescriptiveLabel() ;
		currFilename = change.getUpdatedFilename() ; 
		prevFilename = change.getPreviousFilename() ;
		
		System.out.println("       change: "+changeDescr+" to file : "+currFilename);
	}
	
}